/*
 * Copyright (c) 2009 by ZDO Corporation.
 * Author: A. Onur Cinar
 * 
 * http://code.google.com/p/zdo-struts2
 * http://www.zdo.com
 * 
 * This file is part of ZDO Struts 2 Applications.
 *
 * ZDO-Struts2 is free software: you can redistribute it and/or
 * modify it under the terms of  the GNU General Public License
 * as published by the Free Software Foundation, either version
 *  3 of the License, or any later version.
 *
 * ZDO-Struts2 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY  or  FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 */

package com.zdo.persistence;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.interceptor.Interceptor;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;

/**
 * Persistence interceptor. Provides JPA support for struts actions.
 * 
 * <ul>
 * <li>Initializes {@link EntityManagerFactory} and closes at
 * application exit.</li>
 * <li>Invokes action in a {@link EntityTransaction} commit on success, 
 * or rolls back on exception.<li>
 * <li>If action is {@link PersistenceAware}, injects current 
 * {@link EntityManager} instance.</li>
 * </ul>
 * 
 * Example:
 * 
 * <pre>
 * &lt;interceptor 
 *     name=&quot;persistence&quot; 
 *     class=&quot;com.zdo.persistence.PersistenceInterceptor&quot; /&gt;
 *     
 * &lt;interceptor-ref name=&quot;persistence&quot;&gt;
 *     &lt;param name=&quot;unitName&quot;&gt;name&lt;/param&gt;
 * &lt;/interceptor&gt;
 * </pre>
 */
public class PersistenceInterceptor implements Interceptor
{
	private static final long serialVersionUID = 2906014927524263449L;

	private static final Logger LOG = LoggerFactory
			.getLogger(PersistenceInterceptor.class);

	private String unitName;
	private EntityManagerFactory entityManagerFactory;

	/**
	 * Sets the unit name.
	 * 
	 * Example:
	 * 
	 * <pre>
	 * &lt;interceptor-ref name=&quot;persistence&quot;&gt;
	 *     &lt;param name=&quot;unitName&quot;&gt;name&lt;/param&gt;
	 * &lt;/interceptor&gt; 
	 * </pre>
	 * 
	 * @param unitName unit name.
	 */
	public void setUnitName (String unitName)
	{
		this.unitName = unitName;
	}

	/**
	 * Destroy interceptor. Closes the {@link EntityManagerFactory} instance.
	 */
	public void destroy ()
	{
		LOG.info("Closing entity manager factory.");
		entityManagerFactory.close();
		entityManagerFactory = null;
	}

	/**
	 * Init interceptor. Get new {@link EntityManagerFactory} instance.
	 */
	public void init ()
	{
		LOG.info("Initiating entity manager factory.");
		entityManagerFactory = Persistence.createEntityManagerFactory(unitName);
	}

	/**
	 * Intercepts the action invocation.
	 * 
	 * @param invocation action invocation.
	 * @throws Exception action exception.
	 */
	public String intercept (ActionInvocation invocation) throws Exception
	{
		// Get current action object
		Object action = invocation.getAction();

		// Skip the non persistence aware actions
		if (!(action instanceof PersistenceAware))
		{
			return invocation.invoke();
		}

		// New entity manager instance
		EntityManager em = entityManagerFactory.createEntityManager();

		// Set action's entity manager
		((PersistenceAware) action).setEntityManager(em);

		String result = null;

		// Invoke action with transaction
		try
		{
			result = invocation.invoke();
		}
		finally
		{
			em.close();
		}

		return result;
	}
}
